package Java8新特性;

import org.junit.Test;

import java.time.LocalTime;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/*
    Stream 的中间操作
       多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而是在终止操作时一次性全部处理,称为"惰性求值"

     筛选与切片
          filter(Predicate p):接收 Lambda,从流中排除某些元素
          distinct():筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
          limit(long maxSize):截断流,使其元素不超过给定数量
          skip(long n):跳过元素,返回一个扔掉了前 n 个元素的流,若流中元素不足 n 个,则返回一个空流,与 limit(n) 互补

     映射
          map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
          peek:如同于map,能得到流中的每一个元素,但map接收的是一个Function表达式,有返回值,而peek接收的是Consumer表达式,没有返回值
          mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream
          mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream
          mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream
          flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

     排序
          sorted():产生一个新流,其中按自然顺序排序
          sorted(Comparator com):产生一个新流,其中按比较器顺序排序

 */
public class StreamAPI06 {

    //筛选与切片
    @Test
    public void test1(){
        List<Employee> list = EmployeeData.getEmployees();
        Stream<Employee> stream = list.stream();

        //filter(Predicate p):接收 Lambda,从流中排除某些元素
        stream.filter( e -> e.getSalary() > 7000).forEach(System.out::println);//查询员工表中工资大于7000的员工信息

        System.out.println("*********************************");

        //limit(long maxSize):截断流,使其元素不超过给定数量  --> Stream一旦关闭需要重新生成
        list.stream().limit(3).forEach(System.out::println);

        System.out.println("*********************************");

        //skip(long n):跳过元素,返回一个扔掉了前 n 个元素的流,若流中元素不足 n 个,则返回一个空流,与 limit(n) 互补
        list.stream().skip(3).forEach(System.out::println);

        System.out.println("*********************************");

        list.add(new Employee(1010, "刘强东", 40, 8000));
        list.add(new Employee(1010, "刘强东", 40, 8000));
        list.add(new Employee(1010, "刘强东", 40, 8000));
        list.add(new Employee(1010, "刘强东", 40, 8000));
        //distinct():筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
        list.stream().distinct().forEach(System.out::println);
    }

    //映射
    @Test
    public void test2(){
        List<Employee> eList = EmployeeData.getEmployees();
        List<String> list = Arrays.asList("aa","bb","cc","dd");

        // map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
        list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);

        // peek:如同于map,能得到流中的每一个元素,但map接收的是一个Function表达式,有返回值,而peek接收的是Consumer表达式,没有返回值
        eList.stream().peek(e -> e.setName("老王")).forEach(System.out::println);

        //练习1:获取员工姓名长度大于3的员工姓名
        Stream<String> nameStream = eList.stream().map(Employee::getName);
        nameStream.filter(e -> e.length() > 3).forEach(System.out::println);

        System.out.println("*********************************");

        //flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流    和map的区别在于它将流给拆开了
        list.stream().flatMap(StreamAPI06::fromStringToStream).forEach(System.out::println);

        /**
         * flatMap详解:把 List 中每个字符串元素以" "分割开,变成一个新的 List
         *      首先 map 方法分割每个字符串元素,但此时流的类型为 Stream<String[]>,因为 split 方法返回的是 String[] 类型,
         *      所以需要使用 flatMap 方法,先使用Arrays::stream将每个 String[] 元素变成一个 Stream 流,然后 flatMap 会将每一个流连接成为一个流,最终返回需要的 Stream
         */
        List<String> strList = new ArrayList<>();
        strList.add("aaa bbb ccc");
        strList.add("ddd eee fff");
        strList.add("ggg hhh iii");
        List<String> str = strList.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(Collectors.toList());
        System.out.println(str);
    }

    //将字符串中的多个字符构成的集合转换为对应的Stream实例
    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> list = new ArrayList<>();
        for(Character c : str.toCharArray()){
            list.add(c);
        }
        return list.stream();
    }

    //排序
    @Test
    public void test3(){
        List<Integer> list = Arrays.asList(43, 21, 64, 2, 19, -3, 35);

        //sorted():产生一个新流,其中按自然顺序排序
        list.stream().sorted().forEach(System.out::println);

        System.out.println("*********************************");

        //sorted(Comparator com):产生一个新流,其中按比较器顺序排序
        List<Employee> eList = EmployeeData.getEmployees();
        eList.stream().sorted((e1,e2) -> {

            int ageValue = Integer.compare(e1.getAge(), e2.getAge());
            if(ageValue != 0){
                return ageValue;
            }else {
                return Double.compare(e1.getSalary(),e2.getSalary());
            }

        }).forEach(System.out::println);
    }

    /**
     * 数值流:Java 8 引入了数值流 IntStream、DoubleStream、LongStream,这种流中的元素都是原始数据类型,分别是 int、double、long
     * 流转换为数值流:
     *      mapToInt(T -> int) : return IntStream
     *      mapToDouble(T -> double) : return DoubleStream
     *      mapToLong(T -> long) : return LongStream
     */
    @Test
    public void test4(){
        List<Employee> list = EmployeeData.getEmployees();

        //转换为数值流
        IntStream intStream = list.stream().mapToInt(Employee::getAge);

        //数值流转换为流:通过 boxed 方法
//        Stream<Integer> ageStream = intStream.boxed();

        /**
         * 数值流常用方法:
         *      sum()
         *      max()
         *      min()
         *      average()
         *      ...
         */
        int sumAge = intStream.sum();
        System.out.println(sumAge);

        /**
         * IntStream 与 LongStream 拥有 range 和 rangeClosed 方法用于数值范围处理,两个方法的区别在于一个是闭区间,一个是半开半闭区间
         * IntStream :
         *   rangeClosed(int, int)
         *   range(int, int)
         * LongStream :
         *   rangeClosed(long, long)
         *   range(long, long)
         *
         * 示例:
         *   rangeClosed(1, 100) : [1, 100]
         *   range(1, 100) : [1, 100)
         */
        IntStream rangeStream = IntStream.range(1, 10);
        rangeStream.boxed().forEach(System.out::println);
    }

}
